#include "lzma/LzmaLib.h"
#include "SWFDecoder.h"
#include "SWF.h"
#include <assert.h>
#include "QtZlib/zlib.h"
#include "SWFTagFactory.h"
#include "TagDecoder.h"

SWFDecoder::SWFDecoder()
	: _swf(nullptr)
{
	_frame = new SWFFrame();
}

SWFDecoder::~SWFDecoder()
{
	if (_frame)
		delete _frame;
}

SWF* SWFDecoder::decode( const std::string& file )
{
	FILE* filePtr;
	fopen_s(&filePtr, file.c_str(), "rb");
	fseek(filePtr, 0, SEEK_END);
	long fileSize = ftell(filePtr);
	char* _data = (char*) malloc(fileSize);
	fseek(filePtr, 0, SEEK_SET);

	size_t totalRead = fread(_data, 1, fileSize, filePtr);
	fclose(filePtr);

	SWFInputStream stream(_data, totalRead);

	_swf = new SWF();
	//read header
	stream.readBytes((char*) _swf->header.Signature, 3);
	_swf->header.Version = stream.readUI8();
	assert(_swf->header.Version >= 9);
	_swf->header.FileLength = stream.readUI32();

	//LZMA
	if (_swf->header.Signature[0] == 'Z')
	{
		char propData[5];
		size_t srcLen = stream.readUI32();
		stream.readBytes(propData, 5);
		srcLen = stream.getRemain();
		char* srcData = (char*) malloc(srcLen);
		stream.readBytes(srcData, srcLen);

		//uncompress
		size_t destLen = _swf->header.FileLength - 8;
		char* dest = (char*) malloc(destLen);
		int result = LzmaUncompress((unsigned char*) dest, &destLen, (unsigned char*) srcData, &srcLen, (unsigned char*) propData, 5);

		free(srcData);
		stream = SWFInputStream(dest, destLen);
	}
	//ZIP
	else if (_swf->header.Signature[0] == 'C')
	{
		size_t srcLen = stream.getRemain();
		char* srcData = (char*) malloc(srcLen);
		stream.readBytes(srcData, srcLen);
		z_uLongf desLen = _swf->header.FileLength - 8;
		char* desData = (char*) malloc(desLen);

		uncompress((z_Bytef*) desData, &desLen, (z_Bytef*) srcData, srcLen);
		free(srcData);
		stream = SWFInputStream(desData, desLen);
	}
	_swf->header.FrameSize = stream.readRect();
	UI16 frameRate = stream.readUI16();
	//fixed 8 number
	_swf->header.FrameRate = frameRate >> 8;
	_swf->header.FrameCount = stream.readUI16();

	TagDecoder tagDecoder(&stream, this);
	tagDecoder.decode();

	return _swf;
}

SWF* SWFDecoder::getSWF() const
{
	return _swf;
}

void SWFDecoder::addDefineTag(UI16 charId, DefineTag* tag)
{
	_dict[charId] = tag;
}

DefineTag* SWFDecoder::getDefineTag(UI16 charId) const
{
	auto itr = _dict.find(charId);
	if (itr == _dict.end())
		return nullptr;
	return itr->second;
}

void SWFDecoder::doABC( TagDoABC* tag )
{
	_frame->doABCs.push_back(tag);
}

void SWFDecoder::any( SWFTag* tag )
{
	_swf->tags.push_back(tag);
}

void SWFDecoder::unknow( TagUnknow* tag )
{
	
}

void SWFDecoder::metadata( TagMetadata* tag )
{
	_swf->metadata = tag;
}

void SWFDecoder::frameLabel( TagFrameLabel* tag )
{
	_frame->frameLabel = tag;
}

void SWFDecoder::setBackgroundColor( TagSetBackgroundColor* tag )
{
	_swf->setBackgroundColor = tag;
}

void SWFDecoder::fileAttributes( TagFileAttributes* tag )
{
	_swf->fileAttributes = tag;
}

void SWFDecoder::end( TagEnd* tag )
{
	
}

void SWFDecoder::scriptLimits( TagScriptLimits* tag )
{
	_swf->scriptLimits = tag;
}

void SWFDecoder::symbolClass( TagSymbolClass* tag )
{
	_frame->symbolClass = tag;
}

void SWFDecoder::importAssets2( TagImportAssets2* tag )
{
	
}

void SWFDecoder::protect( TagProtect* tag )
{
	_swf->protect = tag;
}

void SWFDecoder::exportAssets( TagExportAssets* tag )
{
	
}

void SWFDecoder::enableDebugger( TagEnableDebugger* tag )
{
	_swf->enableDebugger = tag;
}

void SWFDecoder::enableDebugger2( TagEnableDebugger2* tag )
{
	
}

void SWFDecoder::setTabIndex( TagSetTabIndex* tag )
{

}

void SWFDecoder::defineScalingGrid( TagDefineScalingGrid* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineSceneAndFrameLabelData( TagDefineSceneAndFrameLabelData* tag )
{
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineShape( TagDefineShape* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineShape2( TagDefineShape2* tag )
{
	this->defineShape(tag);
}

void SWFDecoder::defineShape3( TagDefineShape3* tag )
{
	this->defineShape(tag);
}

void SWFDecoder::defineShape4( TagDefineShape4* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineButton( TagDefineButton* tag )
{
	assert(false);
	//TODO: refTags
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineButton2( TagDefineButton2* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineButtonCxform( TagDefineButtonCxform* tag )
{
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineButtonSound( TagDefineButtonSound* tag )
{
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineSprite( TagDefineSprite* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineMorphShape( TagDefineMorphShape* tag )
{
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineMorphShape2( TagDefineMorphShape2* tag )
{
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineFont( TagDefineFont* tag )
{
	assert(false);
	//TODO: tagRef
	//TODO: addDefineTag
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineFont2( TagDefineFont2* tag )
{
	assert(false);
	//TODO: tagRef
	//TODO: addDefineTag
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineFont3( TagDefineFont3* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineFont4( TagDefineFont4* tag )
{
	assert(false);
	//TODO: addDefineTag
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineFontInfo( TagDefineFontInfo* tag )
{
	assert(false);
	//TODO: addDefineTag
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineFontInfo2( TagDefineFontInfo2* tag )
{
	assert(false);
	//TODO: addDefineTag
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineFontAlignZones( TagDefineFontAlignZones* tag )
{
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::defineFontName( TagDefineFontName* tag )
{
	tag->putReference(tag->fontCharacter);
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::defineText( TagDefineText* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineText2( TagDefineText2* tag )
{
	assert(false);
	//TODO: tagRef
	//TODO: addDefineTag
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineEditText( TagDefineEditText* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::cSMTextSettings( TagCSMTextSettings* tag )
{
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::showFrame( TagShowFrame* tag )
{
	_swf->frames.push_back(_frame);
	_frame = new SWFFrame();
}

void SWFDecoder::placeObject( TagPlaceObject* tag )
{
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::placeObject2( TagPlaceObject2* tag )
{
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::placeObject3( TagPlaceObject3* tag )
{
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::defineBitsJPEG2( TagDefineBitsJPEG2* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineBitsJPEG3( TagDefineBitsJPEG3* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineBinaryData( TagDefineBinaryData* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::productInfo( TagProductInfo* tag )
{
	_swf->productInfo = tag;
}

void SWFDecoder::removeObject(TagRemoveObject* tag)
{
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::removeObject2(TagRemoveObject2* tag)
{
	_frame->controlTags.push_back(tag);
}

void SWFDecoder::defineBitsLossless( TagDefineBitsLossless* tag )
{
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineBitsLossless2( TagDefineBitsLossless2* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}

void SWFDecoder::defineSound( TagDefineSound* tag )
{
	this->addDefineTag(tag->characterID, tag);
	_frame->exportDefs.push_back(tag);
}
